package parser;
import exceptions.*;
import java.io.*;
import java.util.*;
import java.util.regex.*;
import java.io.*;
import java.util.ArrayList;
import org.w3c.dom.*;
import org.xml.sax.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.apache.tomcat.util.http.fileupload.FileItem;
/**Parser allow to get the list of the email addresses in a .csv, .xml or .ldif file.
 * @author Alberto Magni*/
public class Parser
{
    private ArrayList<String> list=new ArrayList<String>();
//------------------------------------------------------------------------------
    /**Return the list of the email addresses in the file passed
     * @param item the FileItem which contains the file to analize
     * @return an array with the list of email addresses in the file
     * @throws Exception if an error during the parsing of the file occurs
     * @throws IncorrectFileTypeException if the FileItem passed is not of the correct format*/
    public String [] getAddresses(FileItem item) throws IncorrectFileTypeException,MalformedFileException
    {
        String name = item.getName();
        if(!isXML(name)&&!isLDIF(name)&&!isCSV(name))
        {
            throw new IncorrectFileTypeException("The \"To:\" file must be: .csv or .xml or .ldif");
        }
        String file=item.getString();
        String temp[]=null;
        try
        {
            if(isXML(name))
            {
                temp=getXMLAddresses(file);
            }
            else
            {
                if(isCSV(name))
                {
                    temp=getCSVAddresses(file);
                }
                else
                {
                    temp=getLDIFAddresses(file);
                }
            }
        }
        catch(Exception exception)
        {
            throw new MalformedFileException(exception.toString());
        }
        if(temp.length==0)
        {
            throw new MalformedFileException("The file is malformed.");
        }
        else
        {
            return temp;
        }
    }
//------------------------------------------------------------------------------
    /**Trasform the sequence of addresses form an ArrayList to an array.
     * @param list the ArrayList of Strings to be converted in an array
     * @return the converted array*/
    private String [] arrayList2Array(ArrayList<String> list)
    {
        String [] array=new String [list.size()];
        for(int index=0;index<list.size();index++)
        {
            if(list.get(index).startsWith("\""))
            {
                list.set(index,list.get(index).substring(1));
            }
            if(list.get(index).endsWith("\""))
            {
                list.set(index,list.get(index).substring(0,list.get(index).length()-1));
            }
        }
        list.toArray(array);
        return array;
    }
//------------------------------------------------------------------------------
    /**Return the list of the email addresses in the file passed
     * @param file string with the content of the file to be analized, this file is a .csv
     * @return the array with the list of addresses*/
    public String [] getCSVAddresses(String file)
    {
        String [] lines=file.split("\n");
        ArrayList<Integer> positions=new ArrayList<Integer>();
        String separator=new String("");
        String title=new String(lines[0]);
        //get the separator, it could be a , or a ;
        separator=(title.indexOf(";")==-1)?",":";";
        String [] titles=title.split(separator);
        String AddressRegEx=new String("\\b*[eE]-*[mM]ail\\s*\\d*(\\s*[aA]ddress*\\b*)");
        //String Address2RegEx=new String("\\b*[eE]-*[mM]ail\\s*\\d");
        Pattern pattern =Pattern.compile(AddressRegEx);
        //Pattern pattern2 =Pattern.compile(Address2RegEx);
        for(int index=0;index<titles.length;index++)
        {
            Matcher matcher = pattern.matcher(titles[index]);
            //Matcher matcher2 = pattern2.matcher(titles[index]);
            if(matcher.find()/*||matcher2.find()*/)
            {
                positions.add(index);
            }
        }
        for(int index=1;index<lines.length;index++)
        {
            String [] strings=lines[index].split(separator);
            for(int index2=0;index2<positions.size();index2++)
            {
                if(positions.get(index2)<=strings.length&&!strings[positions.get(index2)].equals(""))
                {
                    list.add(strings[positions.get(index2)]);
                }
            }
        }
        return arrayList2Array(list);
    }
//------------------------------------------------------------------------------
    /**Return the list of the email addresses in the file passed
     * @param file string with the content of the file to be analized, this file is a .xml
     * @return the array with the list of addresses*/
    public String [] getXMLAddresses(String file) throws Exception
    {
        DocumentBuilderFactory factory =DocumentBuilderFactory.newInstance();
        DocumentBuilder db = factory.newDocumentBuilder();
        InputSource inStream = new InputSource();
        inStream.setCharacterStream(new StringReader(file));
        Document document = db.parse(inStream);
        Node root = document.getDocumentElement();
        getElements(root,0);
        list.remove(0);
        list=checkForDoubles(list);
        return arrayList2Array(list);
    }
//------------------------------------------------------------------------------
    /**Return the value of a given Node of an XML file
     * @param elem node to check
     * @return the content of the given node*/
    private String getElementValue( Node elem ) 
    {
        Node kid;
        if( elem != null)
        {
            if (elem.hasChildNodes())
            {
                for( kid = elem.getFirstChild(); kid != null; kid = kid.getNextSibling())
                {
                    if( kid.getNodeType() == Node.TEXT_NODE  )
                    {
                        return kid.getNodeValue();
                    }
                }
            }
        }
        return "";
    }
//------------------------------------------------------------------------------
    /**Fill a list with the content of the sublist of a given node
     * @param node root of the tree to analize
     * @param indent the step with the tree is parsed*/
    private void getElements(Node node,int indent) 
    {
        // get element value
        String nodeValue = getElementValue(node);
        list.add(nodeValue.trim());
        // write all child nodes recursively
        NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) 
        {
            Node child = children.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) 
            {
                getElements(child,indent + 2);
            }
        }
    }
//------------------------------------------------------------------------------
    /**Check if there are double elements in a list, if so they are delete
     * @param list the list to be checked and eventually modified
     * @return the list without double elements.*/
    @SuppressWarnings("unchecked")
    private ArrayList<String> checkForDoubles(ArrayList<String> list)
    {
        Set set = new LinkedHashSet();
        set.addAll(list);
        return new ArrayList(set);
    }
//------------------------------------------------------------------------------
    /**Return the list of the email addresses in the file passed
     * @param file string with the content of the file to be analized, this file is a .ldif
     * @return the array with the list of addresses*/
    public String [] getLDIFAddresses(String file) throws Exception
    {
        String [] lines=file.split("\n");
        String AddressRegEx=new String("^\\b*\\w*[eE]*mail:\\s*");
        Pattern pattern =Pattern.compile(AddressRegEx);
        for(int index=0;index<lines.length;index++)
        {
            Matcher matcher = pattern.matcher(lines[index]);
            if(lines[index]!=null&&matcher.find())
            {
                String [] strings = lines[index].split(" ");
                list.add(strings[1]);
            }
        }
        return arrayList2Array(list);
    }
//------------------------------------------------------------------------------
    /**Check if a given file is a .ldif
    * @param filename the complete name of the file to check
    * @return true if the passed file is a .ldif*/
    private boolean isLDIF(String filename)
    {
        String ldifTypeRegEx=new String("\\.[lL][dD][iI][fF]\\b");
        Pattern ldifpattern =Pattern.compile(ldifTypeRegEx);
        Matcher ldifmatcher = ldifpattern.matcher(filename);
        return (ldifmatcher.find());
    }
//------------------------------------------------------------------------------
    /**Check if a given file is a .xml
    * @param filename the complete name of the file to check
    * @return true if the passed file is a .xml*/
    private boolean isXML(String filename)
    {
        String ldifTypeRegEx=new String("\\.[xX][mM][lL]\\b");
        Pattern ldifpattern =Pattern.compile(ldifTypeRegEx);
        Matcher ldifmatcher = ldifpattern.matcher(filename);
        return (ldifmatcher.find());
    }
//------------------------------------------------------------------------------
    /**Check if a given file is a .csv
    * @param filename the complete name of the file to check
    * @return true if the passed file is a .csv*/
    private boolean isCSV(String filename)
    {
        String ldifTypeRegEx=new String("\\.[cC][sS][vV]\\b");
        Pattern ldifpattern =Pattern.compile(ldifTypeRegEx);
        Matcher ldifmatcher = ldifpattern.matcher(filename);
        return (ldifmatcher.find());
    }
}
